
package botlab.level;

import botlab.Entity;
import botlab.Game;
import botlab.command.BaseCommand;
import botlab.control.CameraControl;
import com.jme3.asset.AssetManager;
import com.jme3.input.InputManager;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.logging.Logger;

/**
 *
 * @author Quentin
 */
public abstract class Level
{   
    protected Node rootNode;
    protected Node levelNode;
    protected Node rayCastableNode;
    protected Game app;
    protected AssetManager assetManager;
    protected InputManager inputManager;
//    protected PhysicsSpace space;
    protected TreeMap<String,Entity> entities;
    protected String name;
    protected boolean attached;
    protected boolean loaded;
    protected Spatial mainChar;
    protected ArrayList<Light> sunList;


    public boolean isAttached()
    {
        return attached;
    }

    public boolean isLoaded()
    {
        return loaded;
    }
    
    public Level(String name)
    {
        this.app = Game.getApp();
        this.assetManager = app.getAssetManager();
        this.inputManager=app.getInputManager();
        this.rootNode = app.getRootNode();
//        this.space = app.getStateManager().getState(BulletAppState.class).getPhysicsSpace();
        this.entities=new TreeMap<String,Entity>();
        this.sunList=new ArrayList<Light>();
        
        rayCastableNode = new Node();
        levelNode = new Node(name);
        attached=false;
        loaded=false;
    }
    
    public abstract void load();
    public abstract void start(BaseCommand bc);
    public abstract String getSolution();
    
    
    public String getSolution(String levelName)
    {
        try {
            InputStream inputStream = Level.class.getResourceAsStream("/solutions/"+levelName+".txt");
            if(inputStream==null) return "// Solution not yet defined";
            int size = inputStream.available();
            byte bytes[] = new byte[size];
            if (size != inputStream.read(bytes, 0, size))
            {
                throw new IOException();
            }
            return new String(bytes, "US-ASCII");
        } catch (IOException ex) {
            Logger.getLogger(LevelGoThrough.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        return "// Solution not yet defined";
    }
    
    public Node getRayCastableNode()
    {
        return rayCastableNode;
    }
    
    public Node getLevelNode()
    {
        return this.levelNode;
    }
    
    /*
     * Call cleanup on each entity, remove all chlidrens and lights from levelNode
     * and clear the entity list.
     */
    public void cleanup()
    {
        // Deleting camera control listener
        if(mainChar!=null && mainChar.getControl(CameraControl.class)!=null)
        {
            mainChar.getControl(CameraControl.class).cleanup();
        }
        
        for(Iterator<Entry<String, Entity>> it=entities.entrySet().iterator();it.hasNext();)
        {
            it.next().getValue().cleanup();
        }
        rayCastableNode.detachAllChildren();
        for(int i=0;i<sunList.size();i++)
            levelNode.removeLight(sunList.get(i));
        sunList.clear();
        levelNode.detachAllChildren();
        entities.clear();
        
        // We have to reload the level !
        loaded=false;
    }
    /*
     * Attach all entities declared in the entity list to the levelNode.
     * A rayCastable node is created, entities to be detected (with rayCast) are attached to it
     * and then it's attached to levelNode. Entities that are not expected to be detected
     * are directly attached to levelNode.
     */
    protected void attachEntities()
    {
        for(Iterator<Entry<String, Entity>> it=entities.entrySet().iterator();it.hasNext();)
        {
            Entity ent = (Entity)it.next().getValue();
            if (ent.isRayCastable())
                rayCastableNode.attachChild(ent.getNode());
            else 
                levelNode.attachChild(ent.getNode());
        }
        levelNode.attachChild(rayCastableNode);
    }
    
    /*
     * Detach levelNode from rootNode, i.e it won't be visible.
     */
    public void dettach()
    {
        rootNode.detachChild(levelNode);
        attached=false;
    }

    /*
     * Attach levelNode to rootNode, i.e let it be visible.
     */
    public void attach()
    {
        if(levelNode!=null)
        {
            rootNode.attachChild(levelNode);
            attached=true;
        }
        
    }

    public Spatial getMainChar()
    {
        return mainChar;
    }
    
    public void createLightning()
    {
        AmbientLight sun2 = new AmbientLight();
        sun2.setColor(ColorRGBA.Blue.mult(0.2f));
        sunList.add(sun2);
        DirectionalLight sun3 = new DirectionalLight();
        sun3.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f).normalizeLocal());
        sun3.setColor(ColorRGBA.White.mult(0.1f));
        sunList.add(sun3);
        DirectionalLight sun4 = new DirectionalLight();
        sun4.setDirection(new Vector3f(0f, -1f, 0f).normalizeLocal());
        sun4.setColor(ColorRGBA.White.mult(0.5f));
        sunList.add(sun4);
        AmbientLight sun5 = new AmbientLight();
        sun5.setColor(ColorRGBA.White.mult(0.3f));
        sunList.add(sun5);
        
        levelNode.addLight(sun2);
        levelNode.addLight(sun3);
        levelNode.addLight(sun4);
        levelNode.addLight(sun5);
    }
}
